/******************************************************************************
 * Copyright (C) 2013 - 2018 ShenZhen OnePlus Technology Co.,Ltd All Rights Reserved.
 * 本软件为深圳万普拉斯科技有限公司开发研制。未经本公司正式书面同意，其他任何个人、团体不得使用、复制、修改或发布本软件.
 *****************************************************************************/
package com.data.reports.manager.database;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.lang.StringUtils;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author hujifang
 * @version V1.0
 * @Title: DynamicDataSource.java
 * @Package com.data.reports.manager.database
 * @Description 动态数据源
 * @date 2018 03-16 15:25.
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final String DATA_SOURCES_NAME = "targetDataSources";

    //数据源配置服务
    private DataSourceConfigService dataSourceConfigService;

    private ReentrantLock lock=new ReentrantLock();

    public void setDataSourceConfigService(DataSourceConfigService dataSourceConfigService) {
        this.dataSourceConfigService = dataSourceConfigService;
    }

    @SuppressWarnings("unchecked")
    private Map<Object, Object> getTargetDataSources() throws NoSuchFieldException, IllegalAccessException {
        Field field = AbstractRoutingDataSource.class.getDeclaredField(DATA_SOURCES_NAME);
        field.setAccessible(true);
        return (Map<Object, Object>) field.get(this);
    }

    @Override
    protected String determineCurrentLookupKey() {
        String dataSourceName = DataSourceContextHolder.getDbType();
        if (StringUtils.isBlank(dataSourceName)) {
            // 默认的数据源名字
            dataSourceName = DataSourceContextHolder.DEFAULT;
        }
        try {
            Map<Object,Object> targetDataSources=getTargetDataSources();
            if(targetDataSources ==null){
                targetDataSources=new HashMap<>();
            }
            DataSource dataSource=(DataSource)targetDataSources.get(dataSourceName);
            if(dataSource==null){
                // 从已创建的数据库中获取要访问的数据库，如果没有则创建一个
                selectDataSource(targetDataSources,dataSourceName);
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
        return dataSourceName;
    }

    /**
     * 该方法为同步方法，防止并发创建两个相同的数据源 使用双检锁的方式，防止并发
     * @param dbType 数据库配置编号
     */
    private void selectDataSource(Map<Object,Object> targetDataSources,String dbType) {
        try {
            lock.lock();
            // 再次从数据库中获取，双检锁
            DataSource dataSource = (DataSource)targetDataSources.get(dbType);
            if (dataSource==null) {
                // 为空则创建数据库
                dataSource = createDataSource(dbType);
                if (null != dataSource) {
                    DataSourceContextHolder.setDbType(dbType);
                    targetDataSources.put(dbType,dataSource);
                    super.afterPropertiesSet();
                } else {
                    throw new IllegalStateException("创建数据源失败.");
                }
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 查询对应数据库的信息
     * @param dbType 数据库配置编号
     * @return 数据源
     */
    private DataSource createDataSource(String dbType) {
        String originalType = DataSourceContextHolder.getDbType();
        // 先切换回主库
        DataSourceContextHolder.setDbType(DataSourceContextHolder.DEFAULT);
        // 查询所需信息
        DataSourceConfig dataSourceConfig = dataSourceConfigService.getDataSourceConfigByCode(dbType);
        if (dataSourceConfig==null){
            throw new IllegalStateException("DataSource is error.");
        }
        // 切换回目标库
        DataSourceContextHolder.setDbType(originalType);
        return createDataSource(dataSourceConfig.getUrl(),
                                dataSourceConfig.getUsername(),
                                dataSourceConfig.getPassword());
    }

    /**
     * 创建数据源
     * @param url 数据库连接
     * @param username 用户名
     * @param password 密码
     * @return 数据源
     */
    private DataSource createDataSource(String url, String username, String password) {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(DbDriverClassUtil.getDriverClassByUrl(url));
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setInitialSize(2);
        dataSource.setMaxActive(5);
        dataSource.setMaxIdle(2);
        dataSource.setTimeBetweenEvictionRunsMillis(1000);
        dataSource.setTestWhileIdle(true);
        return dataSource;
    }
}
